<template>
  <div class="arc-body">
    <view class="arc-container" @touchstart.stop="onMouseMove" @touchmove.stop="onMouseMove">
      <view class="arc-circle" />
      <view
        class="arc-dot"
        :style="{
          top: `${dotPosition?.y - dotR}px`,
          left: `${dotPosition?.x - dotR}px`,
        }"
      />
      <view class="custom-box">
        <slot />
      </view>
    </view>

    <div class="right-content">
      <view class="opacity-container">
        <div class="opacity-lable">
          <span>透明度: </span>
          <span>{{ opacity }}%</span>
        </div>
        <slider :value="opacity" min="0" max="100" :block-size="12" active-color="#F25A27" style="margin: 0" @change="sliderChange" />
      </view>
    </div>
  </div>
</template>

<script lang="ts" setup>
import { ref, computed, getCurrentInstance, nextTick, onMounted } from 'vue';

/* _____________⬇️rpx转px⬇️_____________ */

const RpxToPx = (rpx: number) => {
  const { screenWidth } = uni.getSystemInfoSync();
  return (screenWidth / 750) * rpx;
};

/* _____________⬇️参数配置⬇️_____________ */

interface Config {
  size?: number; // 元素直径(rpx)
  color?: string; // 默认颜色(例：rgb(255, 255, 0))
  dotSize?: number; // 滑块直径(rpx)
  lineWidth?: number; // 环形线宽(rpx)
}
const props = withDefaults(defineProps<Config>(), {
  size: 250,
  dotSize: 40,
  lineWidth: 20,
});
const { size, color, dotSize, lineWidth } = props;
const r = RpxToPx(size / 2), // 半径
  dotR = RpxToPx(dotSize / 2), // 滑块半径
  A = { x: r, y: r }, // 圆心坐标
  O = { x: r, y: r * (lineWidth / size) }; // 0度坐标点
const emit = defineEmits<{ (e: 'change', color: string): void; (e: 'update:color', color: string): void }>();

/* _____________⬇️css bind⬇️_____________ */

const dotStyle = ref(`${RpxToPx(dotSize)}px`);
const boxSize = computed(() => `${RpxToPx(size)}px`);
const maskSize = computed(() => `${RpxToPx((size - lineWidth * 2) / 2)}px`);

/* _____________⬇️颜色列表⬇️_____________ */

const colors = [
  { s: [0, 255, 255, 1], e: [0, 255, 0, 1] },
  { s: [0, 255, 0, 1], e: [255, 255, 0, 1] },
  { s: [255, 255, 0, 1], e: [255, 0, 0, 1] },
  { s: [255, 0, 0, 1], e: [255, 0, 255, 1] },
  { s: [255, 0, 255, 1], e: [0, 0, 255, 1] },
  { s: [0, 0, 255, 1], e: [0, 255, 255, 1] },
];

/* _____________⬇️当前颜色rgb值⬇️_____________ */

const nowColor = computed(() => {
  const { x, y } = dotPosition.value;
  const nowAngle = calcAngleReadOnly(x, y) || 0;
  let nowRGB: number[] = [255, 0, 0],
    nowIndex = Math.floor(nowAngle / 60),
    tempColor = colors[nowIndex],
    lastRatio = (nowAngle % 60) / 60;
  nowIndex = nowIndex > 5 ? 5 : nowIndex;
  if (nowAngle % 60 === 0) {
    nowRGB = colors[nowIndex].s || colors[0].s;
  } else {
    for (let i = 0; i < nowRGB.length; i++) {
      if (tempColor.s[i] == tempColor.e[i]) {
        nowRGB[i] = tempColor.s[i];
        continue;
      }
      if (tempColor.s[i] > tempColor.e[i]) {
        nowRGB[i] = 255 - Math.abs(tempColor.s[i] - tempColor.e[i]) * lastRatio;
      } else {
        nowRGB[i] = Math.abs(tempColor.s[i] - tempColor.e[i]) * lastRatio;
      }
    }
  }
  return `rgba(${Math.floor(nowRGB[0])},${Math.floor(nowRGB[1])},${Math.floor(nowRGB[2])},${opacity.value / 100})`;
});

/* _____________⬇️计算当前颜色所在角度（回显）⬇️_____________ */
const calcRatio = (rgb?: string) => {
  if (!rgb) return 0;
  // const color = rgb.replace(/[^0-9,]/g, '').split(',');
  const color = rgb.match(/[\d.]+/g).slice(0, 3);
  let resRatio: number = 0;
  if (color.length !== 3) {
    throw 'rgb颜色值错误';
  } else {
    console.log('---------------------------------', color);
    for (let i = 0; i < colors.length; i++) {
      const tempColors = colors[i];
      const ss1 = +color[0] >= tempColors.s[0] && +color[0] <= tempColors.e[0];
      const ee1 = +color[0] <= tempColors.s[0] && +color[0] >= tempColors.e[0];
      const ss2 = +color[1] >= tempColors.s[1] && +color[1] <= tempColors.e[1];
      const ee2 = +color[1] <= tempColors.s[1] && +color[1] >= tempColors.e[1];
      const ss3 = +color[2] >= tempColors.s[2] && +color[2] <= tempColors.e[2];
      const ee3 = +color[2] <= tempColors.s[2] && +color[2] >= tempColors.e[2];
      const first = ss1 || ee1,
        second = ss2 || ee2,
        third = ss3 || ee3;
      if (first && second && third) {
        const nowPosition = colors.indexOf(tempColors);
        for (let j = 0; j < 3; j++) {
          if (tempColors.s[j] != tempColors.e[j]) {
            const isLoss = tempColors.s[j] - tempColors.e[j] > 0;
            const sRatio = isLoss ? (tempColors.s[j] - +color[j]) / 255 : +color[j] / 255;
            resRatio = (nowPosition + sRatio) * 60;
          }
        }
      }
    }
    opacity.value = rgb.match(/[\d.]+/g)[3] * 100;
    console.log('---------------------------------', opacity.value);
  }
  return resRatio;
};

/* _____________⬇️触摸和滑动事件⬇️_____________ */

const onMouseMove = async (e: any) => {
  // #ifdef H5
  e.preventDefault();
  // #endif
  console.log('onMouseMove', e);
  const { changedTouches } = e;
  const x = changedTouches[0].pageX;
  const y = changedTouches[0].pageY;
  const { top, left } = arcOffset.value;
  await calcAngle(x - left, y - top);
  console.log('updateColor', nowColor.value);
  emit('change', nowColor.value);
  emit('update:color', nowColor.value);
};

/* _____________⬇️计算当前所在角度⬇️_____________ */

const calcAngle = (x: number, y: number) => {
  const AB = Math.sqrt(Math.pow(A.x - O.x, 2) + Math.pow(A.y - O.y, 2)),
    AC = Math.sqrt(Math.pow(A.x - x, 2) + Math.pow(A.y - y, 2)),
    BC = Math.sqrt(Math.pow(O.x - x, 2) + Math.pow(O.y - y, 2)),
    cosA = (Math.pow(AB, 2) + Math.pow(AC, 2) - Math.pow(BC, 2)) / (2 * AB * AC);
  console.log('触摸的点到圆心的距离超过范围', AC, r, r * 1.2, r * 0.6);
  // 触摸的点到圆心的距离超过范围
  if (AC > r * 1.2 || AC < r * 0.6) {
    console.log('触摸的222222222222222222', AC, r, r * 1.2, r * 0.6);
    return;
  }

  let angleA: number = Math.round((Math.acos(cosA) * 180) / Math.PI);
  // 判断夹角在顺时针还是逆时针
  const dx = x - A.x;
  const dy = y - A.y;
  if ((O.x - A.x) * dy - (O.y - A.y) * dx < 0) {
    angleA = 360 - angleA;
  }
  angleA = angleA === 360 ? 0 : angleA;
  calcDotPosition(angleA);

  return angleA;
};

// 添加一个新的只读版本的 calcAngle 方法
const calcAngleReadOnly = (x: number, y: number) => {
  const AB = Math.sqrt(Math.pow(A.x - O.x, 2) + Math.pow(A.y - O.y, 2)),
    AC = Math.sqrt(Math.pow(A.x - x, 2) + Math.pow(A.y - y, 2)),
    BC = Math.sqrt(Math.pow(O.x - x, 2) + Math.pow(O.y - y, 2)),
    cosA = (Math.pow(AB, 2) + Math.pow(AC, 2) - Math.pow(BC, 2)) / (2 * AB * AC);

  // 触摸的点到圆心的距离超过范围
  if (AC > r * 1.2 || AC < r * 0.6) return;

  let angleA: number = Math.round((Math.acos(cosA) * 180) / Math.PI);
  // 判断夹角在顺时针还是逆时针
  const dx = x - A.x;
  const dy = y - A.y;
  if ((O.x - A.x) * dy - (O.y - A.y) * dx < 0) {
    angleA = 360 - angleA;
  }
  angleA = angleA === 360 ? 0 : angleA;

  // 注意：这里不调用 calcDotPosition

  return angleA;
};

/* _____________⬇️计算滑块所在坐标点⬇️_____________ */

const dotPosition = ref({ x: 0, y: 0 });
const calcDotPosition = (angleA: number) => {
  const cosA = Math.cos((angleA * 2 * Math.PI) / 360),
    sinA = Math.sin((angleA * 2 * Math.PI) / 360),
    x = (O.x - A.x) * cosA - (O.y - A.y) * sinA + A.x,
    y = (O.y - A.y) * cosA + (O.x - A.x) * sinA + A.y;

  console.error('calcDotPosition', x, y);
  dotPosition.value = { x, y };
};

/* _____________⬇️获取元素偏移量⬇️_____________ */
const arcOffset = ref({ top: 0, left: 0 }); // 容器偏移量
const init = async () => {
  console.error('init');
  // #ifdef MP
  const query = uni.createSelectorQuery().in(getCurrentInstance());
  // #endif
  // #ifndef MP
  const query = uni.createSelectorQuery();
  // #endif
  await query.select('.arc-circle').boundingClientRect((res: any) => {
    const { top, left } = res;
    arcOffset.value = { top, left };
  });
  await query.exec();
  await nextTick();
  await calcDotPosition(calcRatio(color));
};

const opacity = ref(100);
function sliderChange(e) {
  opacity.value = e.detail.value;
  emit('change', nowColor.value);
  emit('update:color', nowColor.value);
}

onMounted(() => init());
</script>

<style lang="scss" scoped>
.arc-body {
  width: 100%;
  display: flex;
  //flex-direction: column;
  justify-content: space-between;
  align-items: center;
  gap: 20rpx;
}
.arc-container {
  $size: v-bind(boxSize);
  $mSize: v-bind(maskSize);
  position: relative;
  width: $size;
  height: $size;
  //margin: 0 auto 20rpx;
  display: flex;
  align-items: center;
  justify-content: center;

  .arc-circle {
    $size: v-bind(boxSize);
    width: $size;
    height: $size;
    border-radius: 50%;
    mask: radial-gradient(transparent $mSize, #000 $mSize);
    background: conic-gradient(aqua, lime, yellow, red, magenta, blue, aqua);
  }

  .arc-dot {
    $dotColor: v-bind(nowColor);
    $dotSize: v-bind(dotStyle);
    position: absolute;
    z-index: 3;
    width: $dotSize;
    height: $dotSize;
    border-radius: 50%;
    background: $dotColor;
    border: calc($dotSize / 2 * 0.15) solid #fff;
    box-shadow: 0 0 10rpx rgba(105, 104, 104, 0.3);
  }

  .custom-box {
    $cSize: calc($mSize * 2);
    top: 50%;
    left: 50%;
    width: $cSize;
    height: $cSize;
    overflow: hidden;
    position: absolute;
    border-radius: 50%;
    transform: translate(-50%, -50%);
    display: flex;
    align-items: center;
    justify-content: center;
  }
}

.right-content {
  display: flex;
  flex-direction: column;
  justify-content: space-around;
  gap: 20rpx;
  padding: 0 40rpx 0 0;
  .now-color {
    display: flex;
    align-items: center;
    gap: 20rpx;
    .now-color-box {
      width: 142rpx;
      height: 36rpx;
    }
  }
}

.opacity-container {
  width: 280rpx;
  margin: 0 auto;
  .opacity-lable {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin: 0 0 20rpx;
  }
}
</style>
